import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * UDP 搜索者，用于搜索服务方
 */
public class UDPSearcher {
    private static final int LISTEN_PORT = 30000; // 监听端口号

    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.println("UDPProvider Started.");

        /**
         * 监听
         * 1、随时结束
         */
        Listener listener = listen();
        sendBroadcast();

        // 读取任意字符，都可以退出
        System.in.read();
        List<Device> devices = listener.getDevicesAndClose();

        // 局域网内可能有多个设备
        for (Device device : devices) {
            System.out.println("Device:" + device.toString());
        }

        /**
         * 完成
         */
        System.out.println("UDPSearcher Finished");
    }

    private static Listener listen() throws InterruptedException {
        System.out.println("UDPSearcher start listen.");

        // 构建Listener，在这之前，构建countDownLatch
        CountDownLatch countDownLatch = new CountDownLatch(1);
        Listener listener = new Listener(LISTEN_PORT, countDownLatch);
        listener.start();

        countDownLatch.await(); // 等待listener启动完成
        return listener;
    }

    private static void sendBroadcast() throws IOException {
        System.out.println("UDPProvider sendBroadcast Started.");

        // 作为搜索方，无须指定一个端口，系统直接分配
        DatagramSocket datagramSocket = new DatagramSocket();

        /**
         * 构建一份request数据
         */
        String requestData = MessageCreator.buildWithPort(LISTEN_PORT);
        byte[] requestDataBytes = requestData.getBytes(); // 未设定字符集
        // 直接根据发送者构建一个回送报文
        DatagramPacket requestPacket =  new DatagramPacket(requestDataBytes,
                requestDataBytes.length);
        requestPacket.setAddress(InetAddress.getByName("255.255.255.255")); // 广播端口
        requestPacket.setPort(20000); // 端口是20000

        datagramSocket.send(requestPacket); // 发送
        datagramSocket.close();


        /**
         * 完成
         */
        System.out.println("UDPSearcher sendBroadcast Finished");
        datagramSocket.close();
    }

    private static class Device {
        final int port;
        final String ip;
        final String sn;

        public Device(int port, String ip, String sn) {
            this.port = port;
            this.ip = ip;
            this.sn = sn;
        }

        @Override
        public String  toString() {
            return "Device{" +
                    "port=" + port +
                    ", ip='" + ip + '\'' +
                    ", sn='" + sn + '\'' +
                    '}';
        }
    }

    private static class Listener extends Thread {
        private final int listenPort;
        private final CountDownLatch countDownLatch; // 让外界感知，Listener已经启动了
        private final List<Device> devices = new ArrayList<>();
        private boolean done = false;
        private DatagramSocket ds = null; // 搜索者

        public Listener(int listenPort, CountDownLatch countDownLatch) {
            this.listenPort = listenPort;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            super.run();

            /**
             * 通知已启动
             */
            countDownLatch.countDown();
            try {
                ds = new DatagramSocket(listenPort);

                while (!done) {
                    /**
                     * 接收UDP Provider回送的数据
                     */
                    // 构建接收实体
                    final byte[] buf = new byte[512];
                    DatagramPacket receivePack = new DatagramPacket(buf, buf.length);
                    ds.receive(receivePack); // 接收
                    // 处于监听（or阻塞）状态，要等broadcast发送完，且服务器回送数据报文，才能让listener继续执行下去

                    // 打印接收到的信息与发送者的信息
                    // 发送者的IP地址
                    String ip = receivePack.getAddress().getHostAddress();
                    // 发送者的端口
                    int port = receivePack.getPort();
                    // 数据大小
                    int dataLen = receivePack.getLength();

                    String data = new String(receivePack.getData(), 0, dataLen); // 假设报文内容都是String
                    System.out.println("UDPSearcher receive from ip:" + ip + "\tport:" + port + "\tdata:" + data);

                    /**
                     * 解析sn
                     */
                    String sn = MessageCreator.parseSn(data);
                    if (sn != null) {
                        Device device = new Device(port, ip, sn);
                        devices.add(device);
                    }
                }

            } catch (Exception e) {

            } finally {
                close();
            }

            System.out.println("UDPSearcher listener finished");
        }

        private void close() {
            if (ds != null) {
                ds.close();
                ds = null;
            }
        }

        /**
         * 拿devices并关闭
         * @return
         */
        List<Device> getDevicesAndClose() {
            this.done = true;
            close();
            return devices;
        }
    }
}
